<?php
namespace org\phpstruct\db\ORM;

use org\phpstruct\core;
use org\phpstruct\db;
use org\phpstruct\db\I;
use org\phpstruct\db\PDO;

class Pager extends Util {

    const RES_PER_PAGE = 20;
    const PAGE = 0;

    private $countStmt;
    public $res_per_page,
    $page,
    $found_pages,
    $found_items,
    $prev_page,
    $next_page,
    $count_field,
    $results;

    public function __construct(\PDOStatement $qcount,$res_per_page=self::RES_PER_PAGE,$page=self::PAGE,$count_field='tot') {

        if(!$qcount instanceof \PDOStatement) {
            throw new ORMPagerNotValidStatementException();
        }

        $this->countStmt = $qcount;
        $this->res_per_page = $res_per_page;
        $this->page = $page;
        $this->count_field = $count_field;

        $this->build();
    }//:-

    private function build() {

        try {

            if(!$this->countStmt->execute()) {

                $err = $this->countStmt->errorInfo();
                throw new \PDOException($err[2],$err[1]);
            }

            /**
             * TOTALE ELEMENTI TROVATI
             */
            $this->found_items = $this->countStmt->fetchColumn();

            if(!is_numeric($this->found_items)) {
                Logger::log(sprintf('[%s] - line:%s - PAGES COUNT RETRIVIED IS NOT A NUMBER... CHECK YOUR PDOSTATEMNT!!!',__CLASS__,__LINE__));
                throw new ORMPagerNotValidStatementException();
            }

            /**
             * PAGINE TROVATE
             */
            $this->found_pages = ceil(($this->found_items / $this->res_per_page));

            /**
             * CURRENT PAGE
             */
            if($this->page > $this->found_pages) {
                $this->page = 0;
            } elseif ($this->page < 0) {
                $this->page = 0;
            }

            /**
             * NEXT PAGE
             */
            if($this->page < $this->found_pages) {
                $this->next_page = $this->page + 1;
            } elseif ($this->page == $this->found_pages) {
                $this->next_page = null;
            } else {
                $this->next_page = null;
            }

            /**
             * PREV PAGE
             */
            if($this->page > 0) {
                $this->prev_page = $this->page - 1;
            } elseif ($this->page == 0) {
                $this->prev_page = null;
            } elseif ($this->page > $this->found_pages) {
                $this->prev_page = $this->found_pages;
            } else {
                $this->prev_page = null;
            }

        } catch (Exception $e) {

            Logger::log($e);
            throw $e;
        }

    }//:-

    /**
     * Get a Pager Object
     *
     * @param $classname
     * @param array $data
     * @param int $res_per_page
     * @param int $page
     * @param string $join
     * @param string $orderby
     * @return Rebel_DB_ORM_Pager
     */
    public static function getPagerByData($classname,$data,$res_per_page=20,$page=1,$join='',$extrawhere='',$orderby='',$extraselect = '',$extrabind=null) {

        $data = parent::dtoToArray($data);
        $db = PDO\Db::getInstance();
        $r = new ReflectionClass($classname);
        $c = $r->getConstants();

        $entityKeys = parent::getEntityKeys($classname);

        $where = parent::getWhereClause($classname,$data,false,false,true);

        switch ($db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
            case 'mysql':
                $qp = sprintf('select %s count(%s) from %s %s %s %s',
                    $cache ? 'SQL_CACHE' : 'SQL_NO_CACHE',
                    $entityKeys[0], //prendo un campo che sicuramente esiste
                    $r->getConstant(parent::MAPPING_TABLE),
                    $join,
                    $where,
                    $where ? ($extrawhere ? "and $extrawhere" : null) : ($extrawhere ? "where $extrawhere" : null)
                );
                break;
            case 'oci':
                $qp = sprintf('select count(%s.%s) from %s %s %s %s',
                    $r->getConstant(parent::MAPPING_TABLE),
                    $entityKeys[0], //prendo un campo che sicuramente esiste
                    $r->getConstant(parent::MAPPING_TABLE),
                    $join,
                    $where,
                    $where ? ($extrawhere ? "and $extrawhere" : null) : ($extrawhere ? "where $extrawhere" : null)
                );
                break;
        }

        //		print_r($qp);

        $pstmt = $db->prepare($qp);

        //		print_r($r->getProperties());

        foreach ($data as $k => $v) {
            if($r->getProperty($k)) {
                $pstmt->bindValue(':'.$k,$v,parent::$typemap[gettype($v)]);
            }
        }

        //		exit;

        if(is_array($extrabind)) {
            foreach($extrabind as $k => $v) {
                $pstmt->bindValue(':'.$k,$v,parent::$typemap[gettype($v)]);
            }
        }

        $pager = new Rebel_DB_ORM_Pager($pstmt,$res_per_page,$page ? $page : 1);

        //		die(print_r($pager,1));

        $current = $pager->current() ? $pager->current() - 1 : 0;

        switch ($db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
            case 'mysql':
                $q = sprintf('select %s %s from %s %s %s %s %s limit %s,%s',
                    $cache ? 'SQL_CACHE' : 'SQL_NO_CACHE',
                    $extraselect ? $extraselect : '*',
                    $r->getConstant(self::MAPPING_TABLE),
                    $join,
                    $where,
                    $where ? ($extrawhere ? "and $extrawhere" : null) : ($extrawhere ? "where $extrawhere" : null),
                    $orderby ? $orderby : '',
                    $current*$res_per_page,
                    $res_per_page
                );
                break;
            case 'oci':
                $currentpage = $current * $res_per_page;
                $offset = $currentpage + $res_per_page;
                $q = sprintf('SELECT * FROM (SELECT ROWNUM rnum, a.* from (select %s from %s %s %s %s  %s ) a WHERE rownum<=%d ) WHERE rnum > %d',
                    $extraselect ? $extraselect : '*',
                    $r->getConstant(self::MAPPING_TABLE),
                    $join,
                    $where,
                    $where ? ($extrawhere ? "and $extrawhere" : null) : ($extrawhere ? "where $extrawhere" : null),
                    $orderby,
                    $offset,
                    $currentpage);
                break;
        }
        //        xdebug_var_dump($q);

        $entity = $r->newInstance($data);

        $stmt = $db->prepare($q);

        foreach ($data as $k => $v) {

            if($r->getProperty($k)) {
                $stmt->bindValue(':'.$k,$v,self::$typemap[gettype($v)]);
            }
        }
        if(is_array($extrabind)) {
            foreach($extrabind as $k => $v) {
                $stmt->bindValue(':'.$k,$v,parent::$typemap[gettype($v)]);
            }
        }
        if(!$stmt->execute()) {
            $err = $stmt->errorInfo();
            throw new EntityException($err[2],$err[1]);
        }

        $rs = array();
        while($o = $stmt->fetchObject()) {
            array_push($rs,$o);
        }

        $pager->setResults(count($rs) > 1 ? $rs : array_shift($rs));

        return $pager;

    }//:-

    public function setResults($results) {
        $this->results = $results;
    }//:-

    public function getResults() {
        if($this->results) {
            return is_array($this->results) ? $this->results : array($this->results);
        }
        return null;
    }//:-

    public function next() {
        return $this->next_page;
    }//:-

    public function prev() {
        return $this->prev_page;
    }//:-

    public function getLink($page,$prefix=null) {
        $str = $prefix ? $prefix : "";
        $sep = "?";
        foreach ($_GET as $k => $v) {
            if($k == "page") continue;
            if(is_array($v)) {
                foreach ($v as $key => $val) {
                    $str.="{$sep}{$k}[{$key}]=".urlencode($val);
                }
            } else {
                $str.="{$sep}{$k}=".urlencode($v);
            }
            $sep = "&";
        }

        return $str.= "{$sep}page=$page";
    }//:-

    public function current() {
        return $this->page;
    }//:-

    public function totPages() {
        return $this->found_pages;
    }//:-

    public function totItems() {
        return $this->found_items;
}//:-
}//:-


class ORMPagerNotValidStatementException extends PDOException {

    const MSG = 'Supplied value is not a valid PDO STATEMENT OBJECT';

    public function __construct() {
        parent::__construct(self::MSG);
        Logger::log($this);
    }
}
